﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Argos.Scheduling
{
	/// <summary>
	/// Defines an entry in a schedule.
	/// </summary>
	/// <remarks>
	/// An entry defines the occurrences in a schedule. An entry could for instance
	/// specify that there is an occurrence in the schedule on every monday from 08:00
	/// to 10:00, or that there is an occurrence on the 15th of every month.
	/// </remarks>
	[Serializable]
	public abstract class ScheduleEntry : IEquatable<ScheduleEntry>
	{

		#region public static bool operator ==(ScheduleEntry entry1,ScheduleEntry entry2)

		/// <summary>
		/// Determines whether the two given entry instances are equal.
		/// </summary>
		/// <param name="entry1">The entry to compare to <paramref name="entry2"/>.</param>
		/// <param name="entry2">The entry to compare to <paramref name="entry1"/>.</param>
		public static bool operator ==(ScheduleEntry entry1, ScheduleEntry entry2)
		{
			return string.Equals(string.Format("{0}", entry1), string.Format("{0}", entry2));
		}

		#endregion

		#region public static bool operator !=(ScheduleEntry entry1, ScheduleEntry entry2)

		/// <summary>
		/// Determines whether the two given entry instances are not equal.
		/// </summary>
		/// <param name="entry1">The entry to compare to <paramref name="entry2"/>.</param>
		/// <param name="entry2">The entry to compare to <paramref name="entry1"/>.</param>
		public static bool operator !=(ScheduleEntry entry1, ScheduleEntry entry2)
		{
			return !string.Equals(string.Format("{0}", entry1), string.Format("{0}", entry2));
		}

		#endregion



		#region public Occurrence CurrentOccurrence { get; }

		/// <summary>
		/// Returns the current occurrence that the schedule entry defines.
		/// </summary>
		/// <remarks>
		/// If there is no current occurrence active defined by this schedule
		/// entry, this property returns null.
		/// </remarks>
		public Occurrence CurrentOccurrence
		{
			get { return this.GetOccurrence(DateTime.Now); }
		}

		#endregion
		


		#region public override bool Equals(object obj)

		/// <summary>
		/// Compares the current schedule entry to the given object.
		/// </summary>
		/// <param name="obj">The object to compare the current instance to.</param>
		public override bool Equals(object obj)
		{
			if (obj is ScheduleEntry)
			{
				return this.Equals((ScheduleEntry)obj);
			}

			return false;
		}

		#endregion

		#region public bool Equals(ScheduleEntry other)

		/// <summary>
		/// Compares the given schedule entry to the current.
		/// </summary>
		/// <param name="other">The schedule entry to compare to the current.</param>
		public bool Equals(ScheduleEntry other)
		{
			return this == other;
		}

		#endregion
		
		#region public override int GetHashCode()

		/// <summary>
		/// Returns the hash code for the current instance.
		/// </summary>
		public override int GetHashCode()
		{
			return this.ToString().GetHashCode();
		}

		#endregion

		#region public Occurrence GetNextOccurrence()

		/// <summary>
		/// Returns the occurrence defined by the entry that starts next.
		/// </summary>
		/// <remarks>
		/// If there is no next occurrence defined by the entry, this method
		/// returns null.
		/// </remarks>
		public Occurrence GetNextOccurrence()
		{
			return this.GetNextOccurrence(DateTime.Now);
		}

		#endregion

		#region public virtual Occurrence GetNextOccurrence(DateTime after)

		/// <summary>
		/// Returns the occurrence defined by the entry that starts after
		/// the given date and time.
		/// </summary>
		/// <param name="after">The date and time after which to return the next starting occurrence.</param>
		/// <remarks>
		/// If there is no next occurrence defined by the entry, this method
		/// returns null.
		/// </remarks>
		public virtual Occurrence GetNextOccurrence(DateTime after)
		{
			return null;
		}

		#endregion
		
		#region public Occurrence GetOccurrence(DateTime at)

		/// <summary>
		/// Returns the occurrence that is active at the given date and time.
		/// </summary>
		/// <param name="at">The date and time at which to return the occurrence.</param>
		public Occurrence GetOccurrence(DateTime at)
		{
			Occurrence o = this.GetPreviousOccurrence(at);
			if (null != o && o.Start.ToLocalTime().Add(o.Duration) > DateTime.Now)
			{
				return o;
			}

			return null;
		}

		#endregion

		#region public Occurrence GetPreviousOccurrence()

		/// <summary>
		/// Returns the occurrence defined by the entry that was the previous
		/// occurrence to start. The occurrence may still be active.
		/// </summary>
		/// <remarks>
		/// If there is no previous occurrence defined by the entry, this method
		/// returns null.
		/// </remarks>
		public Occurrence GetPreviousOccurrence()
		{
			return this.GetPreviousOccurrence(DateTime.Now);
		}

		#endregion
		
		#region public virtual Occurrence GetPreviousOccurrence(DateTime before)

		/// <summary>
		/// Returns the occurrence defined by the entry that was the last entry
		/// to start before the given date and time. The occurrence may still
		/// be active.
		/// </summary>
		/// <param name="before">The date and time before which to start the last starting occurrence.</param>
		/// <remarks>
		/// If there is no previous occurrence defined by the entry, this method
		/// returns null.
		/// </remarks>
		public virtual Occurrence GetPreviousOccurrence(DateTime before)
		{
			return null;
		}

		#endregion

	}
}
